//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Base/Include/BaseObject.h"

namespace Engine
{
namespace File
{
	typedef RCryptFile< SimpleFileCryptAlgorithm< 16 > >	SimpleRCryptFile;
	typedef WCryptFile< SimpleFileCryptAlgorithm< 16 > >	SimpleWCryptFile;

}	// File

namespace Platforms
{

	// define ResourceFile in platform specific files
	class ResourceFile;
	SHARED_POINTER( ResourceFile );



	//
	// File System
	//

	class IFileSystem : public BaseObject
	{
	// methods
	public:
		IFileSystem (EngineSubSystemsRef ess);
		~IFileSystem ();

		bool OpenForRead (uni_c_string filename, RFilePtr &file) const;
		bool OpenForWrite (uni_c_string filename, WFilePtr &file) const;
		bool OpenForAppend (uni_c_string filename, WFilePtr &file) const;
		bool CreateFile (uni_c_string filename, WFilePtr &file) const;

		bool OpenForDecrypt (uni_c_string filename, RFilePtr &file, uni_c_string password) const;
		bool CreateEncryptFile (uni_c_string filename, WFilePtr &file, uni_c_string password) const;

		bool CreateMemFile (WFilePtr &file, usize reserve = 0) const;
		bool SaveMemFile (uni_c_string filename, const WFilePtr &file) const;
		bool ReadToMem (uni_c_string filename, RFilePtr &file) const;
		bool ReadToMem (const RFilePtr &fromFile, RFilePtr &toFile) const;
		bool ReadToMemIfSmall (const RFilePtr &fromFile, RFilePtr &toFile) const;

		bool CopyFile (const RFilePtr &fromFile, const WFilePtr &toFile) const;
		bool CopyFile (uni_c_string fromFilename, uni_c_string toFilename) const;


		bool DeleteFile (uni_c_string filename);

		bool FindAndSetCurrentDir (uni_c_string dir, uint searchDepth = 4) const;
		bool SetCurrentDirectory (uni_c_string dir) const;
		bool GetCurrentDirectory (OUT string &dir) const;

		bool OpenResource (uni_c_string resname, RFilePtr &file) const;

		bool IsFileExist (uni_c_string filename) const;
		bool IsResourceExist (uni_c_string filename) const;
		bool IsDirectoryExist (uni_c_string dirname) const;

		bool NewDirectory (uni_c_string dir) const;		// create directory in current directory
		bool CreateDirectories (uni_c_string path) const;	// create directories for path

		bool DeleteEmptyDirectory (uni_c_string dir) const;
		bool DeleteDirectory (uni_c_string dir) const;

		uint64 GetFreeSpace (uni_c_string path) const;
		uint64 GetTotalSpace (uni_c_string path) const;
		
		bool SearchFile (uni_c_string file, uint depth, OUT string &result) const;
		bool SearchFileForward (uni_c_string file, uint depth, OUT string &result) const;
		bool SearchFileBackward (uni_c_string file, uint depth, OUT string &result) const;

		bool SearchDir (uni_c_string dir, uint depth, OUT string &result) const;
		bool SearchDirForward (uni_c_string dir, uint depth, OUT string &result) const;
		bool SearchDirBackward (uni_c_string dir, uint depth, OUT string &result) const;
	};

	
/*
=================================================
	constructor
=================================================
*/
	inline IFileSystem::IFileSystem (EngineSubSystemsRef ess) : BaseObject(ess)
	{
		ESS()->SetFileSystem( this );
	}
	
/*
=================================================
	destructor
=================================================
*/
	inline IFileSystem::~IFileSystem ()
	{
		ESS()->SetFileSystem( null );
	}

/*
=================================================
	OpenForRead
=================================================
*/
	inline bool IFileSystem::OpenForRead (uni_c_string filename, RFilePtr &file) const
	{
		HddRFilePtr	rfile = File::HddRFile::New();

		if ( IsFileExist( filename ) )
		{
			CHECK_ERR( rfile->Open( filename ) );
		}
		else
		{
			RETURN_ERR( "can't find file \"" << filename << '"' );
		}

		file = rfile;
		return true;
	}

/*
=================================================
	OpenForWrite
=================================================
*/
	inline bool IFileSystem::OpenForWrite (uni_c_string filename, WFilePtr &file) const
	{
		HddWFilePtr wfile = File::HddWFile::New();
		
		if ( IsFileExist( filename ) )
		{
			CHECK_ERR( wfile->Open( filename ) );
		}
		else
		{
			RETURN_ERR( "can't find file \"" << filename << '"' );
		}

		file = wfile;
		return true;
	}
	
/*
=================================================
	OpenForAppend
=================================================
*/
	inline bool IFileSystem::OpenForAppend (uni_c_string filename, WFilePtr &file) const
	{
		HddWFilePtr wfile = File::HddWFile::New();
		
		if ( IsFileExist( filename ) )
		{
			CHECK_ERR( wfile->Open( filename, File::HddWFile::APPEND ) );
		}
		else
		{
			RETURN_ERR( "can't find file \"" << filename << '"' );
		}

		file = wfile;
		return true;
	}

/*
=================================================
	CreateFile
=================================================
*/
	inline bool IFileSystem::CreateFile (uni_c_string filename, WFilePtr &file) const
	{
		HddWFilePtr wfile = File::HddWFile::New();
		
		if ( not wfile->Open( filename ) )
		{
			RETURN_ERR( "can't create file \"" << filename << '"' );
		}

		file = wfile;
		return true;
	}
	
/*
=================================================
	OpenForDecrypt
=================================================
*/
	inline bool IFileSystem::OpenForDecrypt (uni_c_string filename, RFilePtr &file, uni_c_string password) const
	{
		RFilePtr	rfile;

		if ( IsFileExist( filename ) )
		{
			CHECK_ERR( OpenForRead( filename, rfile ) );
		}
		else
		if ( IsResourceExist( filename ) )
		{
			CHECK_ERR( OpenResource( filename, rfile ) );
		}
		else
		{
			RETURN_ERR( "can't find file: " << filename );
		}
		
		using namespace File;
		SHARED_POINTER( SimpleRCryptFile );

		SimpleRCryptFilePtr	cfile = new SimpleRCryptFile( rfile, SimpleRCryptFile::CryptAlgorithm( password ) );
		file = cfile;
		return true;
	}
	
/*
=================================================
	CreateEncryptFile
=================================================
*/
	inline bool IFileSystem::CreateEncryptFile (uni_c_string filename, WFilePtr &file, uni_c_string password) const
	{
		WFilePtr	wfile;

		CHECK_ERR( CreateFile( filename, wfile ) );
		
		using namespace File;
		SHARED_POINTER( SimpleWCryptFile );

		SimpleWCryptFilePtr	cfile = new SimpleWCryptFile( wfile, SimpleWCryptFile::CryptAlgorithm( password ) );
		file = cfile;
		return true;
	}
	
/*
=================================================
	CreateMemFile
=================================================
*/
	inline bool IFileSystem::CreateMemFile (WFilePtr &file, usize reserve) const
	{
		MemWFilePtr	wfile = File::MemWFile::New();

		CHECK_ERR( wfile->Create( reserve ) );

		file = wfile;
		return true;
	}
	
/*
=================================================
	SaveMemFile
=================================================
*/
	inline bool IFileSystem::SaveMemFile (uni_c_string filename, const WFilePtr &file) const
	{
		CHECK_ERR( file->GetType() == File::EFile::MEMORY );

		WFilePtr	wfile;
		CHECK_ERR( CreateFile( filename, wfile ) );

		CHECK_ERR( file.To< MemWFilePtr >()->Save( wfile ) );
		return true;
	}
	
/*
=================================================
	ReadToMem
=================================================
*/
	inline bool IFileSystem::ReadToMem (uni_c_string filename, RFilePtr &file) const
	{
		RFilePtr	rfile;
		CHECK_ERR( OpenForRead( filename, rfile ) );
		
		MemRFilePtr	mfile;
		CHECK_ERR( (mfile = File::MemRFile::New( rfile )).IsNotNull() );

		file = mfile;
		return true;
	}
	
/*
=================================================
	ReadToMem
=================================================
*/
	inline bool IFileSystem::ReadToMem (const RFilePtr &fromFile, RFilePtr &toFile) const
	{
		CHECK_ERR( fromFile.IsNotNull() and fromFile->IsOpened() );

		MemRFilePtr	mfile;
		CHECK_ERR( (mfile = File::MemRFile::New( fromFile )).IsNotNull() );

		toFile = mfile;
		return true;
	}
	
/*
=================================================
	ReadToMemIfSmall
=================================================
*/
	inline bool IFileSystem::ReadToMemIfSmall (const RFilePtr &fromFile, RFilePtr &toFile) const
	{
		CHECK_ERR( fromFile.IsNotNull() and fromFile->IsOpened() );

		const usize	max_size = 1 << 20;	// 1Mb

		usize	size = fromFile->Size();

		if ( size > max_size )
		{
			toFile = fromFile;
			return false;
		}

		MemRFilePtr	mfile;
		CHECK_ERR( (mfile = File::MemRFile::New( fromFile )).IsNotNull() );

		toFile = mfile;
		return true;
	}
	
/*
=================================================
	CopyFile
=================================================
*/
	inline bool IFileSystem::CopyFile (const RFilePtr &fromFile, const WFilePtr &toFile) const
	{
		uint8_array_t	buf( 1 << 20 );		// 1Mb buffer
		const usize		rsize			= fromFile->RemainingSize();
		usize			size			= 0;
		
		while ( size < rsize )
		{
			const isize readn  = fromFile->ReadBuf( buf.ptr(), buf.Size() );
			const bool	writen = toFile->Write( buf.ptr(), readn );
			
			CHECK_ERR( readn > 0 and writen );

			size += readn;
		}

		CHECK_ERR( size == rsize );
		return true;
	}

/*
=================================================
	CopyFile
=================================================
*/
	inline bool IFileSystem::CopyFile (uni_c_string fromFilename, uni_c_string toFilename) const
	{
		RFilePtr	rfile;
		WFilePtr	wfile;

		CHECK_ERR( OpenForRead( fromFilename, rfile ) );
		CHECK_ERR( OpenForWrite( toFilename, wfile ) );

		return CopyFile( rfile, wfile );
	}

/*
=================================================
	CreateDirectory
=================================================
*/
	inline bool IFileSystem::CreateDirectories (uni_c_string dir) const
	{
		return _platform_::FileSystem::CreateDirectories( dir );
	}
	
/*
=================================================
	FindAndSetCurrentDir
=================================================
*/
	inline bool IFileSystem::FindAndSetCurrentDir (uni_c_string dirname, uint searchDepth) const
	{
		return _platform_::FileSystem::FindAndSetCurrentDir( dirname, searchDepth );
	}

/*
=================================================
	IsFileExist
=================================================
*/
	inline bool IFileSystem::IsFileExist (uni_c_string filename) const
	{
		return _platform_::FileSystem::IsFileExist( filename );
	}

/*
=================================================
	IsDirectoryExist
=================================================
*/
	inline bool IFileSystem::IsDirectoryExist (uni_c_string dirname) const
	{
		return _platform_::FileSystem::IsDirectoryExist( dirname );
	}
	
/*
=================================================
	NewDirectory
=================================================
*/
	inline bool IFileSystem::NewDirectory (uni_c_string dir) const
	{
		return _platform_::FileSystem::NewDirectory( dir );
	}
	
/*
=================================================
	DeleteEmptyDirectory
=================================================
*/
	inline bool IFileSystem::DeleteEmptyDirectory (uni_c_string dir) const
	{
		return _platform_::FileSystem::DeleteEmptyDirectory( dir );
	}
	
/*
=================================================
	DeleteDirectory
=================================================
*/
	inline bool IFileSystem::DeleteDirectory (uni_c_string dir) const
	{
		return _platform_::FileSystem::DeleteDirectory( dir );
	}
	
/*
=================================================
	DeleteFile
=================================================
*/
	inline bool IFileSystem::DeleteFile (uni_c_string filename)
	{
		return _platform_::FileSystem::DeleteFile( filename );
	}
	
/*
=================================================
	SetCurrentDirectory
=================================================
*/
	inline bool IFileSystem::SetCurrentDirectory (uni_c_string dir) const
	{
		return _platform_::FileSystem::SetCurrentDirectory( dir );
	}
	
/*
=================================================
	GetCurrentDirectory
=================================================
*/
	inline bool IFileSystem::GetCurrentDirectory (OUT string &dir) const
	{
		return _platform_::FileSystem::GetCurrentDirectory( dir );
	}
	
/*
=================================================
	SearchFile
=================================================
*/
	inline bool IFileSystem::SearchFile (uni_c_string file, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchFile( file, depth, result );
	}
	
/*
=================================================
	SearchFileForward
=================================================
*/
	inline bool IFileSystem::SearchFileForward (uni_c_string file, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchFileForward( file, depth, result );
	}
	
/*
=================================================
	SearchFileBackward
=================================================
*/
	inline bool IFileSystem::SearchFileBackward (uni_c_string file, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchFileBackward( file, depth, result );
	}
	
/*
=================================================
	SearchDir
=================================================
*/
	inline bool IFileSystem::SearchDir (uni_c_string dir, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchDir( dir, depth, result );
	}
	
/*
=================================================
	SearchDirForward
=================================================
*/
	inline bool IFileSystem::SearchDirForward (uni_c_string dir, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchDirForward( dir, depth, result );
	}
	
/*
=================================================
	SearchDirBackward
=================================================
*/
	inline bool IFileSystem::SearchDirBackward (uni_c_string dir, uint depth, OUT string &result) const
	{
		return _platform_::FileSystem::SearchDirBackward( dir, depth, result );
	}


}	// Platforms
}	// Engine
